from django.db import models
from myaccount.models import User
from django.urls import reverse
from django.conf import settings  # 引入定义字段中SEO设置与自定义User
import markdown
import re
from django.utils.html import strip_tags
from django.utils.functional import cached_property
from django.utils.text import slugify
from markdown.extensions.toc import TocExtension
from mdeditor.fields import MDTextField
from course.models import Course


# 文章关键词，用来作为 SEO 中 keywords
class Keyword(models.Model):
    name = models.CharField('文章关键词', max_length=20)

    class Meta:
        verbose_name = '关键词'
        verbose_name_plural = verbose_name
        ordering = ['name']

    def __str__(self):
        return self.name


# 网站导航菜单栏分类表
class BigCategory(models.Model):
    name = models.CharField('导航大分类', max_length=20)
    # 用作文章的访问路径，每篇文章有独一无二的标识
    slug = models.SlugField(unique=True)  # 此字符串字段可以建立唯一索引
    # 分类页描述
    description = models.TextField('描述', max_length=240, default=settings.SITE_DESCRIPTION,
                                   help_text='用来作为SEO中description,长度参考SEO标准')
    # 分类页Keywords
    keywords = models.TextField('关键字', max_length=240, default=settings.SITE_KEYWORDS,
                                help_text='用来作为SEO中keywords,长度参考SEO标准')

    class Meta:  # 元信息
        # admin中显示的表名称
        verbose_name = '一级导航'
        verbose_name_plural = verbose_name  # 复数形式相同

    def __str__(self):
        return self.name


# 导航菜单分类下的下拉菜单分类
class Category(models.Model):
	# 分类名字
	name = models.CharField('文章分类', max_length=20)
	# 用作分类路径，独一无二
	slug = models.SlugField(unique=True)
	# 分类栏目页描述
	description = models.TextField('描述', max_length=240, default=settings.SITE_DESCRIPTION,help_text='用来作为SEO中description,长度参考SEO标准')
	# 导航菜单一对多二级菜单,django2.0后定义外键和一对一关系的时候需要加on_delete选项，此参数为了避免两个表里的数据不一致问题
	bigcategory = models.ForeignKey(BigCategory,related_name="Category", on_delete=models.CASCADE,verbose_name='大分类')

	class Meta:#元信息
		# admin中显示的表名称
		verbose_name = '二级导航'
		verbose_name_plural = verbose_name
        # 默认排序
		ordering = ['name']

	def __str__(self):
		return self.name

	#返回当前的url（一级分类+二级分类）
	def get_absolute_url(self):
		return reverse('blog:category', kwargs={'slug': self.slug, 'bigslug': self.bigcategory.slug})  #寻找路由为blog:category的url
	#返回当前二级分类下所有发表的文章列表
	def get_article_list(self):
		return Article.objects.filter(category=self)


'''
# 导航菜单分类下的下拉菜单分类
class Category(models.Model):
    """
    分类
    """
    name = models.CharField('分类名称', max_length=32)
    desc = models.CharField("描述", max_length=30, null=True, blank=True)

    class Meta:
        verbose_name = "文章分类"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name
'''

class Tag(models.Model):
    name = models.CharField(max_length=32,unique=True)
    slug = models.CharField(max_length=32,blank=True,help_text="slug在保存时会自动保存为name字段的值")

    class Meta:
        verbose_name = "标签名称"
        verbose_name_plural = verbose_name

    def save(self, *args,**kwargs):
        self.slug = self.name

        super().save(*args, **kwargs)

    def __str__(self):
        return self.name


class Article(models.Model):
    """
    文章
    """
    IMG_LINK = '/static/images/article/default.jpg'
    author = models.ForeignKey(User, verbose_name="作者", on_delete=models.CASCADE)
    title = models.CharField('文章标题', max_length=50, )
    excerpt = models.CharField(max_length=200, blank=True)
    body = MDTextField('文章内容')
    # 文章唯一标识符
    slug = models.SlugField(unique=True)
    tags = models.ManyToManyField(Tag, verbose_name="标签", default='')
    counter = models.PositiveIntegerField(default=0, verbose_name='浏览量', editable=False)
    category = models.ForeignKey(Category, verbose_name='文章分类', on_delete=models.DO_NOTHING)
    created_time = models.DateTimeField('创建时间', auto_now_add=True)  # 当该对象第一次被创建是自动设置该字段为现在日期，创建日期。
    updated_time = models.DateTimeField('修改时间', auto_now=True)  # 保存时自动设置该字段为现在日期，最后修改日期
    # 图片链接
    img_link = models.CharField('图片地址', default=IMG_LINK, max_length=255)
    # 文章关键词多对多文章
    keywords = models.ManyToManyField(Keyword, verbose_name='文章关键词',
                                      help_text='文章关键词，用来作为SEO中keywords，最好使用长尾词，3-4个足够',default='')
    # 关联的课程
    course = models.ForeignKey(Course,blank=True,null=True,on_delete=models.SET_NULL,verbose_name="教程")
    # 文章在教程中的序号，用与给文章排序
    course_sequence = models.PositiveIntegerField(blank=True,null=True,verbose_name="教程序号")

    class Meta:
        # ordering 指定模型返回的数据的排列顺序
        # '-created' 表明数据应该以倒序排列
        ordering = ["-created_time", ]
        verbose_name = "文章"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

    # 统计浏览量
    def increase_views(self):
        self.counter += 1
        self.save(update_fields=['counter'])

    # def save(self,*args,**kwargs):
    #
    #     super().save(*args,**kwargs)

    # 生成url
    # def get_abstruct_url(self):
    def get_absolute_url(self):
        return reverse("article:detail", kwargs={"pk": self.pk})

    # 前篇方法：当前小于文章并倒序排列的第一个
    def get_pre(self):
        return Article.objects.filter(id__lt=self.id).order_by('-id').first()

    # 后篇方法：当前大于文章并正序排列的第一个
    def get_next(self):
        return Article.objects.filter(id__gt=self.id).order_by('id').first()

    def save(self, *args, **kwargs):
        # self.updated_time = timezone.now()
        # 首先实例化一个 Markdown 类，用于渲染 body 的文本。
        # 由于摘要并不需要生成文章目录，所以去掉了目录拓展。
        md = markdown.Markdown(extensions=[
            'markdown.extensions.extra',
            # 'markdown.extensions.codehilite',
            'markdown.extensions.fenced_code',
        ])

        # 先将 Markdown 文本渲染成 HTML 文本
        # strip_tags 去掉 HTML 文本的全部 HTML 标签
        # 从文本摘取前 54 个字符赋给 excerpt
        self.excerpt = strip_tags(md.convert(self.body))[:64]

        super().save(*args, **kwargs)

    def generate_rich_content(a, value):
        md = markdown.Markdown(
            extensions=[
                "markdown.extensions.extra",
                # "markdown.extensions.codehilite",
                'markdown.extensions.fenced_code',
                # 记得在顶部引入 TocExtension 和 slugify
                TocExtension(slugify=slugify),
            ]
        )
        content = md.convert(value)
        m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)
        toc = m.group(1) if m is not None else ""
        return {"content": content, "toc": toc}

    # @property
    # def toc(self):
    #     return self.rich_content.get("toc", "")

    @property
    def body_html(self):
        return self.rich_content.get("content", "")

    @cached_property
    def rich_content(self):
        return self.generate_rich_content(self.body)

    '''
    将方法转为属性，这样就能够以属性访问的方式获取方法返回的值，不过 cached_property 进一步提供缓存功能，
    它将被装饰方法调用返回的值缓存起来，下次访问时将直接读取缓存内容，而不需重复执行方法获取返回结果。
    '''

